package com.songlin.houxin.admin.listeners;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.songlin.houxin.admin.api.entity.Channel;
import com.songlin.houxin.admin.api.entity.ChannelStatistic;
import com.songlin.houxin.admin.api.entity.Order;
import com.songlin.houxin.admin.service.ChannelService;
import com.songlin.houxin.admin.service.ChannelStatisticService;
import com.songlin.houxin.admin.service.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author jinYuLinLang
 * @version 1.0.0
 * @ClassName StatisticsListener.java
 * @Description <p></p>
 * @createTime 2022-03-11 22:20:00
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class StatisticsListener implements ApplicationListener<StatisticsEvent> {

    private final RedisTemplate<String, Object> redisTemplate;
    private final ChannelService channelService;
    private final ChannelStatisticService channelStatisticService;
    private final OrderService orderService;
    private ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();

    @Override
    public void onApplicationEvent(StatisticsEvent event) {
        Object source = event.getSource();
        boolean lock = false;
        try {
            Object o = redisTemplate.boundValueOps(source.toString()).get();
            if (Objects.isNull(o)) {
                lock = true;
                redisTemplate.boundValueOps(source.toString()).set(Thread.currentThread().getId(), 1, TimeUnit.HOURS);
                forkJoinPool.execute(this::statistic);
            }
        } finally {
            if (lock) {
                redisTemplate.delete(source.toString());
            }
        }
    }

    private void statistic() {
        log.info("统计中....");
        List<Channel> channels = channelService.list(new LambdaQueryWrapper<Channel>().eq(Channel::getIsDelete, 0));
        List<Integer> levels = channels.stream().map(Channel::getLevel).distinct().collect(Collectors.toList());
        log.info("levels:{}", JSONUtil.toJsonStr(levels));
        // 倒序排列
        levels.sort(Comparator.reverseOrder());
        // 查询某个渠道下当日的激活数量
        LocalDateTime now = LocalDateTime.now();
        // 当天的开始时间
        LocalDateTime currentDayStart = LocalDateTime.of(now.toLocalDate(), LocalTime.MIN);//当天零点
        // 当月的开始时间
        LocalDateTime monthStartDay = LocalDate.of(now.getYear(), now.getMonth(), 1).atStartOfDay();
        //统计多级级渠道信息
        for (int i = 0; i < levels.size(); i++) {
            // 渠道级别倒序排列先统计级别最小的
            Integer level = levels.get(i);
            // 统计第三季渠道
            List<Channel> currents = channels.stream().filter(channel -> channel.getLevel().equals(level)).collect(Collectors.toList());
            for (Channel current : currents) {
                Integer channelId = current.getId();
                ChannelStatistic channelStatistic = channelStatisticService.getOne(new LambdaQueryWrapper<ChannelStatistic>().eq(ChannelStatistic::getChannelId, channelId));
                // 获取每个渠道当日激活量，
                Integer dayActiveCount = orderService.statisticActiveCount(currentDayStart, channelId);
                // 当月激活量，
                Integer monthActiveCount = orderService.statisticActiveCount(monthStartDay, channelId);
                // 总激活数量
                long count = orderService.count(new LambdaQueryWrapper<Order>().eq(Order::getChannelId, channelId).eq(Order::getDeviceStatus, 1));
                // 当天的返利金额
                Long nowTotalRebate = orderService.totalRebate(channelId, 1, currentDayStart);
                // 当月的返利金额
                Long monthTotalRebate = orderService.totalRebate(channelId, 1, monthStartDay);
                // 总返利金额
                Long totalRebate = orderService.totalRebate(channelId, 1, null);
                //  获取子级渠道的统计列表
                List<Integer> subIds = channels.stream()
                        .filter(channel -> channel.getParentId().equals(channelId))
                        .map(Channel::getId)
                        .collect(Collectors.toList());
                List<ChannelStatistic> subcsList = new ArrayList<>();
                if (CollectionUtil.isNotEmpty(subIds)) {
                    subcsList = channelStatisticService.list(new LambdaQueryWrapper<ChannelStatistic>().in(ChannelStatistic::getChannelId, subIds));
                }
                if (CollectionUtil.isNotEmpty(subcsList)) {
                    dayActiveCount += subcsList.stream().map(ChannelStatistic::getCurrentDateActiveNo).reduce(Integer::sum).orElse(0);
                    monthActiveCount += subcsList.stream().map(ChannelStatistic::getCurrentMonthActiveNo).reduce(Integer::sum).orElse(0);
                    count += subcsList.stream().map(ChannelStatistic::getTotalActiveNo).reduce(Integer::sum).orElse(0);
                    nowTotalRebate += subcsList.stream().map(ChannelStatistic::getCurrentDateRebate).reduce(Integer::sum).orElse(0);
                }

                if (Objects.isNull(channelStatistic)) {
                    channelStatistic = new ChannelStatistic();
                    channelStatistic.setChannelId(channelId);
                    channelStatistic.setChannelLevel(level);
                }
                channelStatistic.setCurrentDateActiveNo(dayActiveCount);
                channelStatistic.setCurrentMonthActiveNo(monthActiveCount);
                channelStatistic.setTotalActiveNo((int) count);
                channelStatistic.setTotalRebate(totalRebate);
                channelStatistic.setCurrentDateRebate(nowTotalRebate.intValue());
                channelStatistic.setCurrentMonthRebate(monthTotalRebate.intValue());
                channelStatisticService.saveOrUpdate(channelStatistic);
            }

        }
        log.info("统计完成.....");


    }

}
